#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>

#include "VersionTag.h"
#include "TableSerialized.h"
#include "../engine/DebugStream.h"

using namespace std;

namespace Client {
namespace Common {

typedef CTableCached<CVersionTag> CTableVersionTag;
typedef CTableSerialized<CVersionTag> CTableVersionTagSerialized;

static string TABLE_NAME = "vertag";

CVersionTag::CVersionTag(JsonValue& v) : mHeadVersion(-1)
{
	try {
		SetValue(v["session"], mSession);
		SetValue(v["dev"], mDevice);
		SetValue(v["name"], mTagName);
		SetValue(v["head"], mHeadVersion);
		SetValue(v["info"], mInfo);
	}
	catch(std::exception&) {
	}
}

CVersionTag::CVersionTag(CDBCursor& c) : CRecord(c)
{
	c.GetString(1, mDevice);
	c.GetString(2, mTagName);
	c.GetInt(3, mHeadVersion);
}

string& CVersionTag::GetTableName()
{
	return TABLE_NAME;
}

void CVersionTag::GetValueSet(map<string, string>& values)
{
	if(mDevice.length() > 0) values["device"] = GetValueDesc(mDevice);
	values["name"] = GetValueDesc(mTagName);
	values["head"] = GetValueDesc(mHeadVersion);
}

void CVersionTag::WriteJson(JsonWriter& writer)
{	
	writer.StartObject();
	writer.String("name").String(mTagName.c_str());
	if(mDevice.length() > 0) writer.String("dev").String(mDevice.c_str());
	if(mHeadVersion > 0) writer.String("head").Int(mHeadVersion);
	if(mInfo.length() > 0) writer.String("info").String(mInfo.c_str());
	if(mSession.length() > 0) writer.String("session").String(mSession.c_str());
	writer.EndObject();
}

TableRef CVersionTag::Query()
{
	return CTableVersionTag::Query(TABLE_NAME, string(), string());
}

VersionTagRef CVersionTag::QueryLocalTag(string& tag)
{
	string s;
	TableRef t = CTableVersionTag::Query(
					TABLE_NAME, 
					string("name=") + GetValueDesc(tag, s) + " and device is null", 
					string()
					);
	if(!t || t->GetCount() == 0) {
		return VersionTagRef();
	}
	
	return boost::dynamic_pointer_cast<CVersionTag, CRecord>(t->NextRecords(1)[0]);
	
}

VersionTagRef CVersionTag::QueryDeviceTag(string& dev, string& tag)
{
	string s1;
	string s2;
	TableRef t = CTableVersionTag::Query(
					TABLE_NAME, 
					string("name=") + GetValueDesc(tag, s1) + " and device=" + GetValueDesc(dev, s2), 
					string()
					);
	if(!t || t->GetCount() == 0) {
		return VersionTagRef();
	}

	return boost::dynamic_pointer_cast<CVersionTag, CRecord>(t->NextRecords(1)[0]);
}

void CVersionTag::AddDeviceTag(VersionTagRef vt)
{
	Engine::DebugStream << "Add device tag - " << vt->mDevice << ':' << vt->mHeadVersion << '\n';
	VersionTagRef oldvt;
	if(vt->mDevice.length() == 0) {
		oldvt = QueryLocalTag(vt->mTagName);
	}
	else {
		oldvt = QueryDeviceTag(vt->mDevice, vt->mTagName);
	}

	if(oldvt) {
		vt->mIndex = oldvt->mIndex;
		//oldvt->mHeadVersion = vt->mHeadVersion;
		CDBHelper::GetInstance().Update(*vt);
	}
	else {
		CDBHelper::GetInstance().Insert(*vt);
	}
}

bool CVersionTag::UpdateVersion(string& tag, CCommitAction& action)
{
	VersionTagRef vt;
	VersionTagRef oldvt;
	oldvt = QueryLocalTag(tag);
	if(oldvt) {
		vt = oldvt;
	}
	else {
		vt = VersionTagRef(new CVersionTag());
		vt->mTagName = tag;
	}

	if(!vt) return false;

	vt->mHeadVersion++;		
	if(!action.doAction(vt->mHeadVersion)) return false;

	if(oldvt) {
		CDBHelper::GetInstance().Update(*vt);
	}
	else {
		CDBHelper::GetInstance().Insert(*vt);
	}

	return true;
}

TableRef CVersionTag::GetSerializedTable(CTable& table)
{
	return CTableVersionTagSerialized::Create(table);
}

TableRef CVersionTag::GetSerializedTable(string& js)
{
	return TableRef(new CTableVersionTagSerialized(js));
}

TableRef CVersionTag::GetSerializedTable()
{
	return TableRef(new CTableVersionTagSerialized());
}

} // Common
} // Client